/**
 * $Id: JedisUtil.java Nov 11, 2014 3:06:27 PM hdp
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.aurora.base.util;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * jedits访问工具
 * @author yongjiang.lin
 *
 */
public final class JedisUtil {
	/**
	 * 日志
	 */
	private static final Logger LOG = LoggerFactory.getLogger(JedisUtil.class);

	/**
	 * 私有构造器.
	 */
	private JedisUtil() {
	}

	private static Map<String, JedisPool> maps = new HashMap<String, JedisPool>();
	/**
	 * 最大活动线程
	 */
	private final static int maxTotal;
	/**
	 * 最大等待线程
	 */
	private final static int maxIdle;
	/**
	 * 最大等待毫秒数
	 */
	private final static int maxWaitMillis;
	/**
	 * 过期时间
	 */
	private final static int timeOut;
	/**
	 * 重试次数
	 */
	private final static int retryNum;
	/**
	 * redis服务器ip
	 */
	private final static String ip;
	/**
	 * 端口
	 */
	private final static int port;
	/**
	 * 密码
	 */
	private final static String pass;
	/**
	 * 在borrow一个jedis实例时，是否提前进行alidate操作；如果为true，则得到的jedis实例均是可用的；
	 */
	private final static boolean testOnBorrow;
	/**
	 * 在return给pool时，是否提前进行validate操作；
	 */
	//private final static boolean testOnReturn;
	
	static {
		InputStream in = JedisUtil.class.getClassLoader().getResourceAsStream("/redis.properties");
		Properties props = new Properties();
		try {
			props.load(in);
			String ipProp = StringUtils.trimToEmpty(props.getProperty("redis.host"));
			String portProp = StringUtils.trimToEmpty(props.getProperty("redis.port"));
			String passProp = StringUtils.trimToEmpty(props.getProperty("redis.pass"));
			String maxTotalProp = StringUtils.trimToEmpty(props.getProperty("redis.maxTotal"));
			String maxIdleProp = StringUtils.trimToEmpty(props.getProperty("redis.maxIdle"));
			String maxWaitMillisProp = StringUtils.trimToEmpty(props.getProperty("redis.maxWaitMillis"));
			String timeOutProp = StringUtils.trimToEmpty(props.getProperty("redis.timeOut"));
			String retryNumProp = StringUtils.trimToEmpty(props.getProperty("redis.retryNum"));
			String testOnBorrowProp = StringUtils.trimToEmpty(props.getProperty("redis.testOnBorrow"));
			//String testOnReturnProp = StringUtils.trimToEmpty(props.getProperty("redis.testOnReturn"));
			
			maxTotal = Integer.parseInt(maxTotalProp);
			maxIdle = Integer.parseInt(maxIdleProp);
			maxWaitMillis = Integer.parseInt(maxWaitMillisProp);
			timeOut = Integer.parseInt(timeOutProp);
			retryNum = Integer.parseInt(retryNumProp);
			ip = ipProp;
			port = Integer.parseInt(portProp);
			pass = passProp;
			testOnBorrow = Boolean.parseBoolean(testOnBorrowProp);
			//testOnReturn = Boolean.parseBoolean(testOnReturnProp);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			throw new ExceptionInInitializerError("redis.properties 文件加载失败");
		}
	}
	
	

	/**
	 * 获取连接池.
	 * 
	 * @return 连接池实例
	 */
	private static JedisPool getPool(String ip, int port) {
		String key = ip + ":" + port;
		JedisPool pool = null;
		if (!maps.containsKey(key)) {
			JedisPoolConfig config = new JedisPoolConfig();
			config.setMaxTotal(maxTotal);
			config.setMaxIdle(maxIdle);
			config.setMaxWaitMillis(maxWaitMillis);
			config.setTestOnBorrow(testOnBorrow);
			//config.setTestOnReturn(testOnReturn);
			try {
				/**
				 * 如果你遇到 java.net.SocketTimeoutException: Read timed out
				 * exception的异常信息 请尝试在构造JedisPool的时候设置自己的超时值.
				 * JedisPool默认的超时时间是2秒(单位毫秒)
				 */
				if (StringUtils.isNotBlank(pass)) {
					pool = new JedisPool(config, ip, port, timeOut, pass);
				} else {
					pool = new JedisPool(config, ip, port, timeOut);
				}
				maps.put(key, pool);
			} catch (Exception e) {
				LOG.error(e.getMessage(), e);
			}
		} else {
			pool = maps.get(key);
		}
		return pool;
	}
	
	/** 
     * 返还到连接池 
     *  
     * @param pool  
     * @param redis 
     */  
    public static void returnResource(JedisPool pool, Jedis redis) {  
        if (redis != null) {  
            pool.returnResource(redis);  
        }  
    } 

	/**
	 * 类级的内部类，也就是静态的成员式内部类，该内部类的实例与外部类的实例 没有绑定关系，而且只有被调用到时才会装载，从而实现了延迟加载。
	 */
	private static class RedisUtilHolder {
		/**
		 * 静态初始化器，由JVM来保证线程安全
		 */
		private static JedisUtil instance = new JedisUtil();
	}

	/**
	 * 当getInstance方法第一次被调用的时候，它第一次读取
	 * RedisUtilHolder.instance，导致RedisUtilHolder类得到初始化；而这个类在装载并被初始化的时候，会初始化它的静
	 * 态域，从而创建RedisUtil的实例，由于是静态的域，因此只会在虚拟机装载类的时候初始化一次，并由虚拟机来保证它的线程安全性。
	 * 这个模式的优势在于，getInstance方法并没有被同步，并且只是执行一个域的访问，因此延迟初始化并没有增加任何访问成本。
	 */
	public static JedisUtil getInstance() {
		return RedisUtilHolder.instance;
	}

	/**
	 * 获取Redis实例.
	 * @param ip redis服务器 ip地址
	 * @param port redis服务器端口
	 * @return Redis工具类实例
	 */
	private Jedis getJedis(String ip, int port) {
		Jedis jedis = null;
		int count = 0;
		do {
			try {
				jedis = getPool(ip, port).getResource();
				// log.info("get redis master1!");
			} catch (Exception e) {
				LOG.error("get redis master1 failed!", e);
				// 销毁对象
				getPool(ip, port).returnBrokenResource(jedis);
			}
			count++;
		} while (jedis == null && count < retryNum);
		return jedis;
	}
	/**
	 * 获取jedis实例
	 * @return
	 */
	public Jedis getJedis() {
		return getJedis(ip, port);
	}
	/**
	 * 释放redis实例到连接池.
	 * @param jedis redis实例
	 * @param ip redis服务器 ip地址
	 * @param port redis服务器端口
	 */
	private void closeJedis(Jedis jedis, String ip, int port) {
		if (jedis != null) {
			getPool(ip, port).returnResource(jedis);
		}
	}
	/**
	 * 关闭redis实例
	 * @param jedis
	 */
	public void closeJedis(Jedis jedis) {
		closeJedis(jedis, ip, port);
	}
	/**
	 * 注销jedis池
	 * @param ip redis服务器 ip地址
	 * @param port redis服务器端口
	 */
	private void destoryJedisPool(String ip, int port) {
		String key = ip + ":" + port;
		JedisPool pool = maps.get(key);
		if (null != pool) {
			maps.remove(key);
			pool.destroy();
		}
	}
	/**
	 * 注销jedis池
	 */
	public void destoryJedisPool() {
		destoryJedisPool(ip, port);
	}
	
	public static void main(String[] args) {
		getInstance().closeJedis(getInstance().getJedis());
	}
}
